from multiprocessing import Process, Pipe
import numpy as np
import threading
from time import sleep
import os

###Graphic thread of the program
import wx

# Define notification event for thread completion
EVT_RESULT_ID = wx.NewId()

def EVT_RESULT(win, func):
	"""Define Result Event."""
	win.Connect(-1, -1, EVT_RESULT_ID, func)

class ResultEvent(wx.PyEvent):
	"""Simple event to carry arbitrary result data."""
	def __init__(self, data):
		"""Init Result Event."""
		wx.PyEvent.__init__(self)
		self.SetEventType(EVT_RESULT_ID)
		self.data = data

class CommTh( threading.Thread ):

   def __init__( self, frame ,conn):

	self.frame=frame
	self.conn=conn	
	threading.Thread.__init__ ( self )
	
   def run ( self ):


	while 1: 
		
		data=self.conn.recv()
		wx.PostEvent(self.frame, ResultEvent(data))
		


def GetBitmap(array):
	if array.ndim==2: #Grayscale images
		height,width=array.shape
		narray=np.empty((height,width,3),'uint8')
		narray[:,:,0]=array
		narray[:,:,1]=array
		narray[:,:,2]=array
	elif array.ndim==3: #RGB images
		height,width,c=array.shape
		narray=array.astype('uint8')
	else:
		assert False, "array should have 2 o 3 dimensions"
		
	image = wx.EmptyImage(width,height)
	image.SetData( narray.tostring())
	return image.ConvertToBitmap() # wx.BitmapFromImage(image)

##########################


class SLMFrame(wx.Frame):
	def __init__(self, *args, **kwds):
		"""
		
		**Arguments**
		
		bar  If set to true show the window border

		"""

		bar=kwds.pop("border",False)
		if not bar:
			kwds["style"] = wx.MAXIMIZE_BOX|wx.FRAME_NO_TASKBAR|wx.NO_BORDER
		
		wx.Frame.__init__(self, *args, **kwds)
		self.bitmap_1 = wx.StaticBitmap(self, -1, wx.NullBitmap)

		#self.__set_properties()
		self.__do_layout()
		# end wxGlade

		EVT_RESULT(self,self.Aux)


	#~ def __set_properties(self):
		#~ self.bitmap_1.SetMinSize((M, N))


	def __do_layout(self):
		# begin wxGlade: MyFrame.__do_layout
		self.sizer_1 = wx.BoxSizer(wx.VERTICAL)
		self.sizer_1.Add(self.bitmap_1, 0, 0, 0)
		self.SetSizer(self.sizer_1)
		self.sizer_1.Fit(self)
		self.Layout()
		# end wxGlade



	def Aux(self,event): 
	
		u=event.data.shape
		M,N=u[0],u[1]
		self.bitmap_1.SetMinSize((N,M))
		self.sizer_1.Fit(self)
		self.Layout()
		#self.bitmap_1.SetBitmap(array2Bitmap(event.data,M,N))
		self.bitmap_1.SetBitmap(GetBitmap(event.data))
		event.Skip()


# end of class MyFrame




class MyApp(wx.App):
	def OnInit(self):
		wx.InitAllImageHandlers()
		self.frame_1 = SLMFrame(None, -1, "")
		self.SetTopWindow(self.frame_1)
		self.frame_1.Show()
		self.frame_1.Move((0,0))
		return 1

class MyAppb(wx.App):
	def OnInit(self):
		wx.InitAllImageHandlers()
		self.frame_1 = SLMFrame(None, -1, "",border=True)
		self.SetTopWindow(self.frame_1)
		self.frame_1.Show()
		return 1


# end of class MyApp



###


def f(conn,border):
	if border:
		app = MyAppb()
	else:
		app = MyApp()
	
	m=CommTh(app.frame_1,conn)
	m.start()
	app.MainLoop()


class SLMManager:
	"""
		Class used to create a borderless window, and to put images on it.
		
		This was designed to easily control SLMs
		"""
	def __init__(self,display=":0.1",border=False):
		"""
		**Arguments:**

		=======	 ====================================================
		display	 Display to be used as graphical output. It is usualy
					the display where the SLM is conected. By 
					default it tries to use :0.1
		border	  If set to true, show the window border
		=======	 ====================================================
		
		"""
		os.environ["DISPLAY"]=display
		self.parent_conn, child_conn=Pipe()
		self.p = Process(target=f, args=(child_conn,border))
		self.p.start()

	def send(self, data):
		"""Sends a numpy array to the configured screen
		
		**Arguments:**
		
		==== ===========================================================
		data Numpy array containing the data tu be displayed in the SLM
			 If the SLM resolution is 1024x768, the array shape should 
			 be: data.shape ---> (768,1024), Following the same standard
			 as pylab plots
		==== ===========================================================
		"""
		self.parent_conn.send(data)
	
	def __del__(self):
		self.p.terminate()
		

if __name__ == '__main__':
	LCD=SLMManager(":0.1")
	n=0

	for i  in range(20):
		a=np.zeros((768,1024))
		a=a+i*10
		#a[100:200,500:]=i*10
		LCD.send(a)
		print "***",i,n
		sleep(0.25)
		n=n+1
	del(LCD)
